package com.framework.auth.config;

import lombok.AllArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
import org.springframework.security.oauth2.provider.ClientDetailsService;
import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices;
import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices;
import org.springframework.security.oauth2.provider.token.DefaultTokenServices;
import org.springframework.security.oauth2.provider.token.TokenEnhancerChain;
import org.springframework.security.oauth2.provider.token.TokenStore;
import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter;

import java.util.Arrays;

/**
 * 创建oauth认证服务器配置AuthorizationServerConfig 配置类，
 * 继承AuthorizationServerConfigurerAdapter
 *
 * 文档：https://www.freesion.com/article/8866854596/
 */
@Configuration
@AllArgsConstructor
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
    /**
     * 令牌持久化配置
     */
    private final TokenStore tokenStore;
    /**
     * 客户端详情服务
     */
    private final ClientDetailsService clientDetailsService;
    /**
     * 认证管理器
     */
    private final AuthenticationManager authenticationManager;
    /**
     * 授权码服务
     */
    private final AuthorizationCodeServices authorizationCodeServices;
    /**
     * jwtToken解析器
     */
    private final JwtAccessTokenConverter jwtAccessTokenConverter;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private UserDetailsService userDetailsServiceImpl;

    /**
     * 颁发令牌的客户端配置：
     */
    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        /**
         * 在内存中添加四个客户端配置（还可以在数据库中配置）：
         * client_1只能使用授权码授权方式和刷新令牌的方式获取令牌
         * client_2只能使用密码授权方式和刷新令牌的方式获取令牌
         * client_3只能使用简化授权方式获取令牌
         * client_4只能使用客户端授权方式获取令牌
         */
        clients
                .inMemory()
                .withClient("client_1")
                .secret(passwordEncoder.encode("secret_1"))
                .accessTokenValiditySeconds(3600)
                .refreshTokenValiditySeconds(604800)
                .authorizedGrantTypes("authorization_code", "refresh_token")
                .redirectUris("http://baidu.com")
                .scopes("all")
                .and()
                .withClient("client_2")
                .secret(passwordEncoder.encode("secret_2"))
                .accessTokenValiditySeconds(3600)
                .refreshTokenValiditySeconds(604800)
                .authorizedGrantTypes("password", "refresh_token")
                .scopes("all")
                .and()
                .withClient("client_3")
                .secret(passwordEncoder.encode("secret_3"))
                .accessTokenValiditySeconds(3600)
                .authorizedGrantTypes("implicit")
                .redirectUris("http://baidu.com")
                .scopes("all")
                .and()
                .withClient("client_4")
                .secret(passwordEncoder.encode("secret_4"))
                .accessTokenValiditySeconds(1)
                .authorizedGrantTypes("client_credentials")
                .scopes("all");
    }

    /**
     * 配置访问令牌端点
     */
    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) {
        endpoints
                // 认证管理器
                .authenticationManager(authenticationManager)
                // 授权码服务
                .authorizationCodeServices(authorizationCodeServices)
                // 令牌管理服务
                .tokenServices(tokenServices())
                .userDetailsService(userDetailsServiceImpl)
                .allowedTokenEndpointRequestMethods(HttpMethod.GET, HttpMethod.POST);
    }

    /**
     * 授权服务器安全配置：
     */
    @Override
    public void configure(AuthorizationServerSecurityConfigurer security) {
        /**
         * 获取令牌不需要认证，校验令牌需要认证，允许表单认证
         */
        security
                .checkTokenAccess("permitAll()")
                .tokenKeyAccess("permitAll()")
                .allowFormAuthenticationForClients();
    }

    /**
     * 令牌服务配置
     *
     * @return 令牌服务对象
     */
    public AuthorizationServerTokenServices tokenServices() {
        DefaultTokenServices tokenServices = new DefaultTokenServices();
        tokenServices.setTokenStore(tokenStore);
        tokenServices.setSupportRefreshToken(true);
        // 令牌增强
        TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain();
        tokenEnhancerChain.setTokenEnhancers(Arrays.asList(jwtAccessTokenConverter));
        tokenServices.setTokenEnhancer(tokenEnhancerChain);
        // 令牌默认有效期2小时
        tokenServices.setAccessTokenValiditySeconds(7200);
        // 刷新令牌默认有效期3天
        tokenServices.setRefreshTokenValiditySeconds(259200);
        return tokenServices;
    }

}
